#include <p30f4013.h>
#include <stdlib.h>
#include <string.h>
#include "Delays.h"
#include "Matrix_LCD.h"
#include "font.h"
#include "picture_data.h"
#include "animation.h"

//--------------------------------------------------------------------------
//		LCD controls
//--------------------------------------------------------------------------

extern unsigned char current_y_addr;
extern unsigned char current_x_addr;


/*
	INPUT: Nothing
	OUTPUT: Nothing
        FUNCTION: Initializes the 128x64 pixel LCD to be ready
                  for accepting commands
*/
void LCD_init(void)
{
    TRISB = 0x00;       //outputs

    LCD_RESET = 0;
    milliseconds(100);
    LCD_RESET = 1;

    LCD_RW = WRITE;
    LCD_DI = INSTRUCTION;
    LCD_ENABLE = 0;
    //milliseconds(500);

    LCD_wait(CHIP1);
    LCD_wait(CHIP2);
    LCD_write_instruction(LCD_POWER(1), CHIP1);
    LCD_write_instruction(LCD_POWER(1), CHIP2);
    LCD_write_instruction(LCD_STARTLINE(0), CHIP1);
    LCD_write_instruction(LCD_STARTLINE(0), CHIP2);


    LCD_clear();

    return;
}


/*
    INPUT: Instruction word, which chip to write to
    OUTPUT: Nothing
    FUNCTION: Writes an instruction to the LCD
            Sequence is:
            Set enable high, set D/I low to enable instructions, make sure
            R/W is low to write to the screen, wait for things to settle,
            put the instruction on the data lines, and bring enable back
            low.
 */

void LCD_write_instruction(unsigned char instruction, unsigned char chip)
{
    TRISB = 0x00;            //Port B is output
    LCD_RW = WRITE;         //tell the LCD i'm writing to it
    LCD_DI = INSTRUCTION;   //writing an instruction
    LCD_CHIP_SEL(chip);
    microseconds(5);

    LCD_DATA = instruction;
    microseconds(5);
    LCD_ENABLE = 1;
    microseconds(5);
    LCD_ENABLE = 0;



    return;
}
/*
    INPUT: Instruction word, chip to write to
    OUTPUT: Nothing
    FUNCTION: Writes the given instruction to the given chip, then
              waits until the LCD no longer returns a busy flag
*/
void LCD_write_instruction_wait(unsigned char instruction, unsigned char chip)
{
    LCD_write_instruction(instruction, chip);
    LCD_wait(chip);
    return;
}


/*
    INPUT: Data word, chip to write to
    OUTPUT: Nothing
    FUNCTION: Writes the given data to the given chip
              and increments the y address
*/
void LCD_write_data(unsigned char data, unsigned char chip)
{
    TRISB = 0x00;       //Port B is output
    LCD_RW = WRITE;     //tell the LCD i'm writing to it
    LCD_DI = DATA;
    LCD_CHIP_SEL(chip);
    microseconds(5);

    LCD_DATA = data;
    microseconds(5);
    LCD_ENABLE = 1;
    microseconds(5);;
    LCD_ENABLE = 0;

    current_y_addr++;
    

    return;
}

/*
    INPUT: Data word, chip to write to
    OUTPUT: Nothing
    FUNCTION: Writes the given data to the given chip, then
              waits until the LCD no longer returns a busy flag
*/
void LCD_write_data_wait(unsigned char data, unsigned char chip)
{
    LCD_write_data(data, chip);
    LCD_wait(chip);
    return;
}



/*
    INPUT: Chip to read from
    OUTPUT: Data word read from the LCD
    FUNCTION: Sets the microcontroller ports to read,
              reads the data from the given LCD chip.
              Returns that data.
*/
unsigned char LCD_read(unsigned char chip)
{
    unsigned char read_data = 0;

    TRISB = 0xFF;       //data lines are now inputs
    LCD_RW = READ;      //tell the LCD i'm reading from it
    LCD_CHIP_SEL(chip); //select the given chip
    milliseconds(5);

    LCD_ENABLE = 1;
    milliseconds(5);
    read_data = LCD_DATA & 0xFF;       //read data is on PORTB
    
    LCD_ENABLE = 0;

    return(read_data);
}


/*
    INPUT: Chip to wait for
    OUTPUT: Nothing
    FUNCTION: Remains in the loop until the given chip no longer
              returns a busy flag
*/

void LCD_wait(unsigned char chip)
{

    while(LCD_read(chip) & (0b10010000));   //If the LCD is busy or reset,
                                            //  keep reading and waiting

    return;
}


/*
    INPUT: Chip to select
    OUTPUT: Nothing
    FUNCTION: Sets the proper control lines to select the given chip
*/
void LCD_CHIP_SEL(unsigned char chip)
{

    if(chip)
    {
        LCD_CS2 = 0;
        LCD_CS1 = 1;
    }
    else
    {
        LCD_CS2 = 1;
        LCD_CS1 = 0;
    }

    return;
}




/*
	INPUT: ASCII character to be printed to LCD
	OUTPUT: Prints the character to the current address of the LCD,
		and increments the current address counter to keep track
                of the y address
*/
void LCD_char(char character, unsigned char chip)
{
    int index=0;

    index = (character - 32) * 5;
    
    LCD_write_data(font_5x7_data[index], chip);
    LCD_write_data(font_5x7_data[index+1], chip);
    LCD_write_data(font_5x7_data[index+2], chip);
    LCD_write_data(font_5x7_data[index+3], chip);
    LCD_write_data(font_5x7_data[index+4], chip);

    milliseconds(75);


    

    return;
}



void LCD_char2(char character, unsigned char chip)
{
    int index=0;

    index = (character - 32) * 5;

    LCD_write_data(font_5x7_data[index], chip);
    LCD_write_data(font_5x7_data[index+1], chip);
    LCD_write_data(font_5x7_data[index+2], chip);
    LCD_write_data(font_5x7_data[index+3], chip);
    LCD_write_data(font_5x7_data[index+4], chip);

    //milliseconds(75);




    return;
}




void LCD_partial_char(char character, unsigned char num_lines)
{
    int index=0;
    unsigned char i = 0;
    unsigned char j = 0;

    index = (character - 32) * 5;

    for(i=0; i<num_lines; i++)
    {
        LCD_write_data(font_5x7_data[index+i], CHIP1);
    }

    for(j=0; j<(5-num_lines); j++)
    {
        current_y_addr = 0;
        LCD_write_data(font_5x7_data[index+j+i], CHIP2);
    }

    
  


    return;
}



/*
    INPUT: Text string to write to the screen, whether to spread
           the string across both chips
    OUTPUT: Nothing
    FUNCTION: Writes the given instruction to the given chip, then
              waits until the LCD no longer returns a busy flag
*/
void LCD_string(char string[], unsigned char num_chips)
{
    unsigned char i = 0;
    unsigned char j = 0;
    unsigned char length = 0;

    length = strlen(string);
    
    for(i=0; i<length; i++)
    {
        
        if(i>=12 && (num_chips==2))
        {
            if(i==12)
            {
                LCD_partial_char(string[i], (64-current_y_addr));
            }
            else
            {
                LCD_char2(string[i], CHIP2);
            }

        }
        else
        {
            LCD_char2(string[i], CHIP1);
        }
        
    }


    return;
}



void LCD_string2(char string[], unsigned char num_chips)
{
    unsigned char i = 0;
    unsigned char j = 0;
    unsigned char chip = CHIP1;
    int length = 0;

    length = strlen(string);

    for(i=0; i<length; i++)
    {

        if(current_y_addr > (64-5))
        {
            LCD_partial_char(string[i], (64-current_y_addr));
            //current_y_addr = 0;

            chip = CHIP2;    

        }
        else
        {
            LCD_char(string[i], chip);
        }

    }


    return;
}


int next_space_addr(char string[], unsigned char i)
{
    int addr = 0;
    unsigned char j = 0;
    int length = 0;

    length = strlen(string);

    for(j=i; j<length; j++)
    {
        if(string[j] == ' ')
        {
            break;
        }
    }

    addr = (current_y_addr + (j-i)*5) + 5;

    return(addr);
}


void LCD_paragraph(char string[])
{
    unsigned char i = 0;
    unsigned char j = 0;
    unsigned char k = 0;
    int length = 0;

    length = strlen(string);


    for(i=0; i<length; i++)
    {

        if(j<=12)
        {
            if(j == 12)
            {
                LCD_partial_char(string[i], (64-current_y_addr));
            }
            else
            {
                LCD_char(string[i], CHIP1);
            }
            j++;
        }
        else
        {
            if(next_space_addr(string, i) >= 64)
            {
                k++;
                LCD_write_instruction(LCD_YADDR(0), CHIP1);
                LCD_write_instruction(LCD_YADDR(0), CHIP2);
                LCD_write_instruction(LCD_XADDR(k), CHIP1);
                LCD_write_instruction(LCD_XADDR(k), CHIP2);

                current_y_addr = 0;

                j = 0;

                i = i-1;
            }
            else
            {
                LCD_char(string[i], CHIP2);
                j++;
            }
        }

    }

    return;
}

//void LCD_paragraph(char string[])
//{
//    unsigned char i = 0;
//    unsigned char j = 0;
//    unsigned char k = 0;
//    int length = 0;
//
//    length = strlen(string);
//
//
//    for(i=0; i<length; i++)
//    {
//
//        if(j<=12)
//        {
//            if(j == 12)
//            {
//                LCD_partial_char(string[i], (64-current_y_addr));
//            }
//            else
//            {
//                LCD_char(string[i], CHIP1);
//            }
//            j++;
//        }
//        else
//        {
//            if(next_space_addr(string, i) >= 128)
//            {
//                k++;
//                LCD_write_instruction(LCD_YADDR(0), CHIP1);
//                LCD_write_instruction(LCD_YADDR(0), CHIP2);
//                LCD_write_instruction(LCD_XADDR(k), CHIP1);
//                LCD_write_instruction(LCD_XADDR(k), CHIP2);
//
//                current_y_addr = 0;
//
//                j = 0;
//
//                i = i-1;
//            }
//            else
//            {
//                LCD_char(string[i], CHIP2);
//                j++;
//            }
//        }
//
//    }
//
//    return;
//}





/*
	INPUT: ASCII character to be printed to LCD
	OUTPUT: Prints the character to the given address of the LCD
			 and resets the current address
*/
void LCDchar_addr(char character, char address)
{

       

	return;
}





/*
	INPUT: An LCD DDRAM address. Bit 7 must always be set to 1
	OUTPUT: Changes the display position to the corresponding input address
			and returns the current address
*/
void LCDaddress(unsigned char addr)
{
	

	return;
}


/*
	INPUT: Current display address
	OUTPUT: Decrements the current address by one and
			 deletes the character at that position
*/
void backspace(unsigned char addr)
{
	

	return;
}



/*
	INPUT: none
	OUTPUT: Clears the entire LCD screen
*/
void LCD_clear(void)
{
    unsigned char x, y;

    for(x=0; x<8; ++x)
    {
        LCD_write_instruction(LCD_YADDR(0), CHIP1);
        LCD_write_instruction(LCD_XADDR(x), CHIP1);

        for(y=0; y<64; ++y)
        {
            LCD_write_data(0, CHIP1);
        }
    }

    for(x=0; x<8; ++x)
    {
        LCD_write_instruction(LCD_YADDR(0), CHIP2);
        LCD_write_instruction(LCD_XADDR(x), CHIP2);

        for(y=0; y<64; ++y)
        {
            LCD_write_data(0, CHIP2);
        }
    }


    LCD_write_instruction(LCD_YADDR(0), CHIP1);
    LCD_write_instruction(LCD_YADDR(0), CHIP2);
    LCD_write_instruction(LCD_XADDR(0), CHIP1);
    LCD_write_instruction(LCD_XADDR(0), CHIP2);


    current_y_addr = 0;
    current_x_addr = 0;

    return;
}


/*
	INPUT: none
	OUTPUT: Puts the LCD cursor back to the home position, the upper left most spot
*/
void LCDhome(void)
{
	

	return;
}


void LCD_fullscreen_picture(unsigned char pic_index)
{
    unsigned char i = 0;
    unsigned char j = 0;

    LCD_clear();

    for(j=0; j<8; j++)
    {
        LCD_write_instruction(LCD_YADDR(0), CHIP1);
        LCD_write_instruction(LCD_YADDR(0), CHIP2);
        LCD_write_instruction(LCD_XADDR(j), CHIP1);
        LCD_write_instruction(LCD_XADDR(j), CHIP2);
        
        for(i=0; i<128; i++)
        {
            if(i>63)
            {
                LCD_write_data(picture[pic_index][j][i], CHIP2);
            }
            else
            {
                LCD_write_data(picture[pic_index][j][i], CHIP1);
            }
        }
    }




    return;
}

void LCD_animate(void)
{
    unsigned char i = 0;
    unsigned char j = 0;
    unsigned char k = 0;

    while(1)
    {

        LCD_clear();

        for(j=0; j<8; j++)
        {
            LCD_write_instruction(LCD_YADDR(0), CHIP1);
            LCD_write_instruction(LCD_YADDR(0), CHIP2);
            LCD_write_instruction(LCD_XADDR(j), CHIP1);
            LCD_write_instruction(LCD_XADDR(j), CHIP2);

            for(i=0; i<128; i++)
            {
                if(i>63)
                {
                    LCD_write_data(animation[k][j][i], CHIP2);
                }
                else
                {
                    LCD_write_data(animation[k][j][i], CHIP1);
                }
            }
        }

        if(k==0)
        {
            k=1;
        }
        else
        {
            k=0;
        }



        LCD_write_instruction(LCD_XADDR(7), CHIP1);
        LCD_write_instruction(LCD_XADDR(7), CHIP2);

        LCD_string(" And now it's tea time!!", BOTH_CHIPS);

        milliseconds(300);

    }



    return;

}